package com.seeTools.next.service.sys.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.seeTools.next.api.bo.sys.MenuMetaInfo;
import com.seeTools.next.api.entity.sys.SysMenu;
import com.seeTools.next.api.entity.sys.SysRole;
import com.seeTools.next.api.entity.sys.SysRoleMenu;
import com.seeTools.next.api.entity.sys.SysUser;
import com.seeTools.next.common.constant.Constants;
import com.seeTools.next.common.core.PageQuery;
import com.seeTools.next.mapper.sys.SysMenuMapper;
import com.seeTools.next.service.BaseServiceImpl;
import com.seeTools.next.service.sys.ISysMenuService;
import com.seeTools.next.service.sys.ISysRoleMenuService;
import com.seeTools.next.service.sys.ISysRoleService;
import com.seeTools.next.service.sys.ISysUserService;
import com.seeTools.next.util.PageQueryHelper;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 系统菜单表 服务实现类
 * </p>
 *
 * @author FallenDust
 * @since 2023-05-04
 */
@Service
public class SysMenuServiceImpl extends BaseServiceImpl<SysMenuMapper, SysMenu> implements ISysMenuService {

    private final List<String> excludeList = Arrays.asList("tenant", "menu");
    @Resource
    private SysMenuMapper sysMenuMapper;
    @Resource
    private PageQueryHelper<SysMenu> pageQueryHelper;
    @Resource
    private ISysRoleService iSysRoleService;
    @Resource
    private ISysRoleMenuService iSysRoleMenuService;
    @Resource
    private ISysUserService iSysUserService;

    @Override
    public Page<SysMenu> pageInfo(PageQuery<SysMenu> pageQuery) {
        SysMenu params = pageQuery.getParams();
        // 根据查询参数组装查询wrapper
        QueryWrapper<SysMenu> queryWrapper = new QueryWrapper<>();
        LambdaQueryWrapper<SysMenu> lambdaQueryWrapper = queryWrapper.lambda();
        return pageQueryHelper.pageInfo(this, pageQuery, queryWrapper);
    }

    @Override
    @Cacheable(cacheNames = Constants.CacheKey.MENU_LIST_USER_ID, key = "#userId", unless = "#result.size() == 0")
    public List<SysMenu> getUserMenu(Long userId) {
        return getUserMenuList(userId);
    }

    private List<SysMenu> getUserMenuList(Long userId) {
        List<SysMenu> menuList = new ArrayList<>();
        SysUser userCache = iSysUserService.getUserCache(userId);
        if (userCache != null && Constants.ADMIN_USERNAME.equals(userCache.getLoginName())) {
            return getAdminMenu();
        }
        // 先获取用户拥有权限的菜单
        // 获取用户的角色列表
        List<SysRole> roleList = iSysRoleService.getRoleListByUserId(userId);
        List<Long> roleIdList = roleList.stream().map(SysRole::getId).distinct().collect(Collectors.toList());
        // 获取角色菜单关联列表
        List<SysRoleMenu> roleMenuList = new ArrayList<>();
        if (!roleIdList.isEmpty()) {
            roleMenuList = iSysRoleMenuService.list(new LambdaQueryWrapper<SysRoleMenu>().in(SysRoleMenu::getRoleId, roleIdList));
        }
        List<Long> menuIdList = roleMenuList.stream().map(SysRoleMenu::getMenuId).distinct().collect(Collectors.toList());
        if (!menuIdList.isEmpty()) {
            menuList = this.list(new LambdaQueryWrapper<SysMenu>().in(SysMenu::getId, menuIdList));
            // 还要把菜单的父菜单加进来
            List<SysMenu> parentMenuList = getParentMenuList(menuList);
            menuList.addAll(parentMenuList);
        }
        if (menuList.isEmpty()) {
            return Collections.emptyList();
        }
        menuList = menuList.stream().filter(item -> !excludeList.contains(item.getPermission())).collect(Collectors.toList());
        // 组装菜单，按照树形结构以及排序
        Map<Long, List<SysMenu>> menuListMap = menuList.stream().collect(Collectors.groupingBy(SysMenu::getParentId));
        return getMenuChildren(menuListMap, 0L, false);
    }


    private List<SysMenu> getParentMenuList(List<SysMenu> menuList) {
        List<Long> parentIdList = menuList.stream().map(SysMenu::getParentId).distinct().collect(Collectors.toList());
        List<Long> menuIdList = menuList.stream().map(SysMenu::getId).distinct().collect(Collectors.toList());
        menuIdList.add(0L);
        // 找出父id不在当前菜单id中的
        Collection<Long> parentIdColl = CollUtil.subtract(parentIdList, menuIdList);
        if (parentIdColl.isEmpty()) {
            return Collections.emptyList();
        }
        List<SysMenu> parentMenuList = this.list(new LambdaQueryWrapper<SysMenu>().in(SysMenu::getId, parentIdColl));
        parentMenuList.addAll(getParentMenuList(parentMenuList));
        return parentMenuList;
    }

    /**
     * 获取子菜单信息
     *
     * @param menuListMap
     * @param parentMenuId
     * @return
     */
    private List<SysMenu> getMenuChildren(Map<Long, List<SysMenu>> menuListMap, Long parentMenuId, boolean isAdmin) {
        List<SysMenu> dataList = new ArrayList<>();
        List<SysMenu> menuList = menuListMap.get(parentMenuId);
        if (menuList == null) {
            return dataList;
        }
        for (SysMenu item : menuList) {
            SysMenu menu = addMetaInfo(item);
            List<SysMenu> menuChildren = getMenuChildren(menuListMap, menu.getId(), isAdmin);
            // 子菜单排序
            menuChildren = menuChildren.stream().sorted(Comparator.comparing(SysMenu::getSort)).collect(Collectors.toList());
            menu.setChildren(menuChildren);
            dataList.add(menu);
        }
        if (!isAdmin) {
            dataList = dataList.stream().filter(item -> !excludeList.contains(item.getPermission())).collect(Collectors.toList());
        }
        // 菜单排序
        dataList = dataList.stream().sorted(Comparator.comparing(SysMenu::getSort)).collect(Collectors.toList());
        return dataList;
    }

    /**
     * 转化菜单meta信息
     *
     * @param menu 菜单
     * @return 菜单
     */
    private SysMenu addMetaInfo(SysMenu menu) {
        MenuMetaInfo meta = MenuMetaInfo.builder().icon(menu.getIcon())
                .title(menu.getTitle())
                .isLink(menu.getLink())
                .isHide(Objects.equals(1, menu.getHide()))
                .isKeepAlive(Objects.equals(1, menu.getKeepAlive()))
                .isAffix(Objects.equals(1, menu.getAffix()))
                .isIframe(Objects.equals(1, menu.getIframe()))
                .build();
        menu.setMeta(meta);
        return menu;
    }

    @Override
    public List<SysMenu> getAdminMenu() {
        List<SysMenu> menuList = sysMenuMapper.selectList(null);
        if (menuList.isEmpty()) {
            return Collections.emptyList();
        }
        // 组装菜单，按照树形结构以及排序
        Map<Long, List<SysMenu>> menuListMap = menuList.stream().collect(Collectors.groupingBy(SysMenu::getParentId));
        return getMenuChildren(menuListMap, 0L, true);
    }

    @Override
    public List<String> getUserAuthBtnList(Long userId) {
        List<SysMenu> menuList = getUserMenu(userId);
        return getAuthBtnList(menuList).stream().distinct().collect(Collectors.toList());
    }

    private List<String> getAuthBtnList(List<SysMenu> menuList) {
        List<String> authBtnList = new ArrayList<>();
        for (SysMenu item : menuList) {
            if (Constants.MenuType.BUTTON.equals(item.getType())) {
                authBtnList.add(item.getPermission());
                continue;
            }
            if (!item.getChildren().isEmpty()) {
                authBtnList.addAll(getAuthBtnList(item.getChildren()));
            }
        }
        return authBtnList;
    }

    @Override
    @CacheEvict(cacheNames = Constants.CacheKey.MENU_LIST_USER_ID, allEntries = true)
    public boolean save(SysMenu entity) {
        return super.save(entity);
    }

    @Override
    @CacheEvict(cacheNames = Constants.CacheKey.MENU_LIST_USER_ID, allEntries = true)
    public boolean removeById(SysMenu entity) {
        return super.removeById(entity);
    }

    @Override
    @CacheEvict(cacheNames = Constants.CacheKey.MENU_LIST_USER_ID, allEntries = true)
    public boolean updateById(SysMenu entity) {
        return super.updateById(entity);
    }
}
